﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ESpace;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;

namespace ClockSphere3D
{
    public partial class FormMain : Form
    {
        Device dvc;
        ECamera Cam;
        EDX.ExMesh mCir;
        EDX.ExMesh mCir1;

        List<CamMoveData> MoveData = new List<CamMoveData>();
        int actMoveData = 0;

        int PosInPlanet = 0;

        public FormMain()
        {
            InitializeComponent();

            //MoveData.Add(new CamMoveData(new Vector3(130, 65, -130), 30, -45, 2));
            MoveData.Add(new CamMoveData(new Vector3(0, 105, -183), 40, 0, 2));
            MoveData.Add(new CamMoveData(new Vector3(0, 1, 0), 10, 0, 2));
            MoveData.Add(new CamMoveData(new Vector3(0, 305, 0), 90, 0, 2));
        }

        #region *** инициализация графики *********************************************

        public void InitializeGraphics()
        {
            try
            {
                /* FormBorderStyle = FormBorderStyle.None;
                 MinimumSize = new System.Drawing.Size(1920, 1090);
                 WindowState = FormWindowState.Maximized; // во весь экран
                 TopMost = true;*/

                // создание устройства
                dvc = EDX.CreareDevice(this, true, OnDeviceReset, false, this.ClientRectangle.Width, this.ClientRectangle.Height);
                dvc.DeviceResizing += new CancelEventHandler(OnResizeDvc);

                // загрузка моделей
                mCir = EDX.LoadMesh(Application.StartupPath + "\\Data\\Cir20.x", Application.StartupPath + "\\Data", false);
                mCir1 = EDX.LoadMesh(Application.StartupPath + "\\Data\\Cir8.x", Application.StartupPath + "\\Data", false);
                mCir1.mt[0].Diffuse = Color.Red;
                mCir1.mt[0].Emissive = Color.Red;


                // устанавливаем камеру
                Cam = new ECamera(dvc, new Vector3(), 0, 0, ((float)this.ClientRectangle.Width / (float)this.ClientRectangle.Height), 45, 0.5f, 10000);
                ResetCamPos();

                // вызываем событие сброса
                OnDeviceReset(dvc, null);

                tmrFrame.Enabled = true;
            }
            catch (Exception ex) { MessageBox.Show("Ошибка InitializeGraphics: " + ex.Message); }
        }

        void OnResizeDvc(Object sender, CancelEventArgs e)
        {
            if (WindowState == FormWindowState.Minimized) e.Cancel = true;
        }

        private void OnDeviceReset(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized) return;

            //FilterTextures();
            #region *** прозрачность *************************
            dvc.RenderState.AlphaBlendEnable = true;
            dvc.RenderState.DestinationBlend = Blend.InvSourceAlpha;
            dvc.RenderState.SourceBlend = Blend.SourceAlpha;
            #endregion *** прозрачность *************************

            // устанавливаем свет
            dvc.Lights[0].Type = LightType.Point; // тип освещения
            dvc.Lights[0].Position = new Vector3(0, 0, 0);
            dvc.Lights[0].Diffuse = Color.White; // цвет
            dvc.Lights[0].Range = 300; // максимальное расстояние распространения света
            /*dvc.Lights[0].Attenuation0 = 0; // затухание
            dvc.Lights[0].Attenuation1 = 0.01f;
            dvc.Lights[0].Attenuation2 = 0.00015f;*/
            dvc.Lights[0].Enabled = true;

            for (int i = 1; i < 6; i++)
            {
                dvc.Lights[i].Type = LightType.Directional; // тип освещения
                dvc.Lights[i].Diffuse = Color.FromArgb(100, 100, 100); // цвет
                dvc.Lights[i].Enabled = true;
            }
            dvc.Lights[1].Direction = new Vector3(0, 0, 1); // направление
            dvc.Lights[2].Direction = new Vector3(0, 0, -1); // направление
            dvc.Lights[3].Direction = new Vector3(1, 0, 0); // направление
            dvc.Lights[4].Direction = new Vector3(-1, 0, 0); // направление
            dvc.Lights[5].Direction = new Vector3(0, -1, 0); // направление

            /* dvc.Lights[2].Type = LightType.Directional; // тип освещения
             dvc.Lights[2].Direction = new Vector3(0, 0, 1); // направление
             dvc.Lights[2].Diffuse = Color.White; // цвет
             dvc.Lights[2].Enabled = true;*/

            // Cam.Lighting = false;
            Cam.AspectRatio = ((float)this.ClientRectangle.Width / (float)this.ClientRectangle.Height);
        }

        // фильтрация текстур для сглаживания
        void FilterTextures()
        {
            // Try to set up a texture minify filter, pick anisotropic first
            if (dvc.DeviceCaps.TextureFilterCaps.SupportsMinifyAnisotropic)
            {
                dvc.SamplerState[0].MinFilter = TextureFilter.Anisotropic;
            }
            else if (dvc.DeviceCaps.TextureFilterCaps.SupportsMinifyLinear)
            {
                dvc.SamplerState[0].MinFilter = TextureFilter.Linear;
            }
            // Do the same thing for magnify filter
            if (dvc.DeviceCaps.TextureFilterCaps.SupportsMagnifyAnisotropic)
            {
                dvc.SamplerState[0].MagFilter = TextureFilter.Anisotropic;
            }
            else if (dvc.DeviceCaps.TextureFilterCaps.SupportsMagnifyLinear)
            {
                dvc.SamplerState[0].MagFilter = TextureFilter.Linear;
            }
        }

        #endregion *** инициализация графики *********************************************

        private void FormMain_Shown(object sender, EventArgs e)
        {
            InitializeGraphics();
            tmrFrame.Start();
        }

        void ResetCamPos()
        {
            Cam.Pos = MoveData[0].DestPos;
            Cam.AngX = MoveData[0].DestAngX;
            Cam.AngY = MoveData[0].DestAngY;
            actMoveData = 1;
        }

        #region *** Рисование ***********************************************************

        float FPS = 0;
        int lastSecFPS;
        int cntFPS;
        double elTime;

        List<PointF> pntSec = new List<PointF>();
        PointF[] pntMin = new PointF[59];
        PointF[] pntHour = new PointF[23];

        double TOTALSec = (DateTime.Now - DateTime.Today).TotalSeconds;

        /// <summary>Формирование данных для отрисовки (в зависимости от пройденного времени)</summary>
        void UpdateFrame()
        {
            elTime = Utility.GetElapsedTime();
            TOTALSec += elTime;
            if ((DateTime.Now.Second) != lastSecFPS)
            {
                FPS = cntFPS;
                cntFPS = 0;
                lastSecFPS = DateTime.Now.Second;
            }
            cntFPS++;

            /*float ActionPoints = Cam.Y * elTime;
            if (cmd[0])
            {
                Cam.X = (float)(Cam.Pos.X + ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY)));
                Cam.Z = (float)(Cam.Pos.Z + ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY)));
            }
            if (cmd[1])
            {
                Cam.X = (float)(Cam.Pos.X - ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY)));
                Cam.Z = (float)(Cam.Pos.Z - ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY)));
            }
            if (cmd[2])
            {
                Cam.AngY -= 100 * elTime;
            }
            if (cmd[3])
            {
                Cam.AngY += 100 * elTime;
            }
            if (cmd[4])
            {
                Cam.AngX -= 100 * elTime;
                if (Cam.AngX < 30) Cam.AngX = 30;
            }
            if (cmd[5])
            {
                Cam.AngX += 100 * elTime;
                if (Cam.AngX > 90) Cam.AngX = 90;
            }
            if (cmd[6])
            {
                Cam.X = (float)(Cam.Pos.X + ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY - 90)));
                Cam.Z = (float)(Cam.Pos.Z + ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY - 90)));
            }
            if (cmd[7])
            {
                Cam.X = (float)(Cam.Pos.X - ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY - 90)));
                Cam.Z = (float)(Cam.Pos.Z - ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY - 90)));
            }
            if (cmd[8])
            {
                Cam.Y += 100 * elTime;
            }
            if (cmd[9])
            {
                Cam.Y -= 100 * elTime;
                if (Cam.Y < 10) Cam.Y = 10;
            }

            if (bRun)
            {
                ChangeGeneration();
                if (bTurbo)
                {
                    ChangeGeneration();
                    ChangeGeneration();
                }
            }*/

            DateTime dt = DateTime.Now;
            double TotalSec = (dt - dt.Date).TotalSeconds;
            TotalSec = TOTALSec;
            double sphRasst = 2;
          
            // секунды
            int speed = 12;
            pntSec.Clear();
            for (int i = speed; i >= 1; i--)
            {
                double k = 60 / (double)i;
                double ang = (TotalSec / k * 360.0);

                PointF pnt = GetOtnPnt(sphRasst, ang);

                 if (i == speed && PosInPlanet == 1) SetCamPlanet(pnt);
                 if (i == 1 && PosInPlanet == 2) SetCamPlanet(pnt);
                 pntSec.Add(pnt);

                sphRasst += 15 / (double)speed;
            }

            for (int i = 59; i >= 1; i--)
            {
                double k = 3600 / (double)i;
                double ang = (TotalSec / k * 360.0);

                PointF pnt = GetOtnPnt(sphRasst, ang);

                if (i == 30 && PosInPlanet == 3) SetCamPlanet(pnt); 
                if (i == 1 && PosInPlanet == 4) SetCamPlanet(pnt);

                pntMin[59-i] = pnt;
                sphRasst += 1.2;
            }

            for (int i = 23; i >= 1; i--)
            {
                double k = 86400 / (double)i;
                double ang = (TotalSec / k * 360.0);

                PointF pnt = GetOtnPnt(sphRasst, ang);
                pntHour[23 - i] = pnt;

                sphRasst += 1.2;

                if (i == 2 && PosInPlanet == 5) SetCamPlanet(pnt);
            }


            if (PosInPlanet != 0)
            {
                Cam.Pos = new Vector3(pntCamPlanet.X, 1.1f, pntCamPlanet.Y);
                Cam.AngY = 90 - (float)GetAngle(pntCamPlanet.X, pntCamPlanet.Y, 0, 0);
                Cam.ResetCam();
            }

            if (Move.IsMove)  CamMove(elTime);
        }

        /// <summary>Таймер отрисовки</summary>
        private void tmrFrame_Tick(object sender, EventArgs e)
        {
            tmrFrame.Stop();

            if (WindowState == FormWindowState.Minimized) return;

            UpdateFrame();

            #region *** рисование ********************
            // очищаем окно с заливкой цветом
            EDX.ClearDraw(System.Drawing.Color.Black);

            dvc.BeginScene();

            EDX.DrawString("FPS: " + FPS.ToString(), 10, 10);
            EDX.DrawString("Cam X: " + Cam.X.ToString(), 10, 25);
            EDX.DrawString("Cam Y: " + Cam.Y.ToString(), 10, 40);
            EDX.DrawString("Cam Z: " + Cam.Z.ToString(), 10, 55);
            EDX.DrawString("Cam AngX: " + Cam.AngX.ToString(), 10, 70);
            EDX.DrawString("Cam AngY: " + Cam.AngY.ToString(), 10, 85);

            dvc.Transform.World = Matrix.Translation(0, 0, 0);
            EDX.DrawMesh(mCir1);

            DrawClockfase();

            //dvc.Transform.World = Matrix.Translation(3, 0, 8);
            //EDX.DrawMesh(mCir);

            RedrawField();

            dvc.EndScene();
            //-----------
            dvc.Present();

            #endregion *** рисование ********************

            tmrFrame.Start();
        }

        void DrawMap()
        {

            int sz = 1000;
            CustomVertex.PositionColored[] v = new CustomVertex.PositionColored[sz * 4];
            int c = sz / 2;
            for (int i = 0; i < sz; i++)
            {
                int n = i * 2;
                v[n].Color = Color.LightGreen.ToArgb();
                v[n].Position = new Vector3(i - c + 0.5f, 0, -c + 0.5f);
                v[n + 1].Color = Color.LightGreen.ToArgb();
                v[n + 1].Position = new Vector3(i - c + 0.5f, 0, c + 0.5f);
            }

            for (int i = 0; i < sz; i++)
            {
                int n = i * 2 + sz * 2;
                v[n].Color = Color.LightGreen.ToArgb();
                v[n].Position = new Vector3(-c + 0.5f, 0, i - c + 0.5f);
                v[n + 1].Color = Color.LightGreen.ToArgb();
                v[n + 1].Position = new Vector3(c + 0.5f, 0, i - c + 0.5f);
            }

            dvc.VertexFormat = CustomVertex.PositionColored.Format;
            dvc.DrawUserPrimitives(PrimitiveType.LineList, sz * 2, v);
        }

        void DrawClockfase()
        {
            for (int i = 0; i < 360; i += 6)
            {
                PointF pnt = GetOtnPnt(120, i);
                dvc.Transform.World = Matrix.Translation(pnt.X, 0, pnt.Y);
                EDX.DrawMesh(mCir1);

                if (i % 30 == 0)
                {
                    pnt = GetOtnPnt(121, i);
                    dvc.Transform.World = Matrix.Translation(pnt.X, 0, pnt.Y);
                    EDX.DrawMesh(mCir1);

                    pnt = GetOtnPnt(122, i);
                    dvc.Transform.World = Matrix.Translation(pnt.X, 0, pnt.Y);
                    EDX.DrawMesh(mCir1);

                    pnt = GetOtnPnt(123, i);
                    dvc.Transform.World = Matrix.Translation(pnt.X, 0, pnt.Y);
                    EDX.DrawMesh(mCir1);
                }
            }
        }

        PointF GetOtnPnt(double Rasst, double Angle)
        {
            return new PointF((float)(Rasst * Cos(-Angle + 90)), (float)(Rasst * Sin(-Angle + 90)));
        }

        void RedrawField()
        {
            // солнце
            //gr.DrawImage(Properties.Resources.Солнце70, pCntr.X - 35, pCntr.Y - 35);

            // секунды
            for (int i = 0; i < pntSec.Count; i++)
            {
                dvc.Transform.World = Matrix.Translation(pntSec[i].X, 0, pntSec[i].Y);
                 EDX.DrawMesh(mCir);
            }

            // минуты
            for (int i = 0; i < pntMin.Length; i++)
            {
                dvc.Transform.World = Matrix.Translation(pntMin[i].X, 0, pntMin[i].Y);
                EDX.DrawMesh(mCir);
            }

            // часы
            for (int i = 0; i < pntHour.Length; i++)
            {
                dvc.Transform.World = Matrix.Translation(pntHour[i].X, 0, pntHour[i].Y);
                EDX.DrawMesh(mCir);

            }
        }

        PointF pntCamPlanet;
        void SetCamPlanet(PointF pnt)
        {
            pntCamPlanet = pnt;
            /*Cam.Pos = new Vector3(pnt.X, 1, pnt.Y);
            Cam.AngY = 90 - (float)GetAngle(pnt.X, pnt.Y, 0, 0);*/
        }

        #endregion *** Рисование ***********************************************************

        /// <summary>sin в градусах</summary>
        static public double Sin(double x) { return Math.Sin((x / 180.0) * Math.PI); }
        /// <summary>cos в градусах</summary>
        static public double Cos(double x) { return Math.Cos((x / 180.0) * Math.PI); }

        static public double GetAngle(double x1, double y1, double x2, double y2)
        {
            if (y2 == y1) { if (x2 >= x1) return 0; else return 180; }
            if (x2 == x1) { if (y2 >= y1) return 90; else return 270; }

            double ret = ATan((y2 - y1) / (x2 - x1));
            if (ret < 0) ret += 180;
            if (y2 < y1) ret += 180;
            return ret;

            /* (проигрываем по времени)
             if (p1 == p2) return 0;
            double x = p2.X - p1.X;
            double y = p2.Y - p1.Y;
            return ACos360(x / Hyp(x, y), (y < 0));
             */
        }

        /// <summary>arcTan в градусах</summary>
        static public double ATan(double x) { return Math.Atan(x) * 180.0 / Math.PI; }

        private void FormMain_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            double ActionPoints = 1;
            if (e.KeyCode == Keys.Up)
            {
                Cam.X = (float)(Cam.Pos.X + ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY)));
                Cam.Z = (float)(Cam.Pos.Z + ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY)));
            }
            if (e.KeyCode == Keys.Down)
            {
                Cam.X = (float)(Cam.Pos.X - ActionPoints * Math.Sin(EDX.ToRadian(Cam.AngY)));
                Cam.Z = (float)(Cam.Pos.Z - ActionPoints * Math.Cos(EDX.ToRadian(Cam.AngY)));
            }
            if (e.KeyCode == Keys.Left)
            {
                Cam.AngY -= 1;
            }
            if (e.KeyCode == Keys.Right)
            {
                Cam.AngY += 1;
            }

            if (e.KeyCode == Keys.W)
            {
                Cam.AngX -= 1;
                if (Cam.AngX < 0) Cam.AngX = 0;
            }
            if (e.KeyCode == Keys.S)
            {
                Cam.AngX += 1;
                if (Cam.AngX > 90) Cam.AngX = 90;
            }

            if (e.KeyCode == Keys.PageUp)
            {
                Cam.Y += (float)ActionPoints;
            }
            if (e.KeyCode == Keys.PageDown)
            {
                Cam.Y -= (float)ActionPoints;
            }

            if (e.KeyCode == Keys.Enter)
            {
                StartMove(MoveData[actMoveData].DestPos, MoveData[actMoveData].DestAngX, MoveData[actMoveData].DestAngY, MoveData[actMoveData].Seconds);
                actMoveData++;
                if (actMoveData >= MoveData.Count) actMoveData = 0;
            }

            if (e.KeyCode == Keys.NumPad0)
            {
                StartMove(MoveData[actMoveData].DestPos, MoveData[actMoveData].DestAngX, MoveData[actMoveData].DestAngY, MoveData[actMoveData].Seconds);
                PosInPlanet = 0;
            }
            if (e.KeyCode == Keys.NumPad1)
            {
                Cam.AngX = 5;
                PosInPlanet = 1;
            }
            if (e.KeyCode == Keys.NumPad2)
            {
                Cam.AngX = 5;
                PosInPlanet = 2;
            }
            if (e.KeyCode == Keys.NumPad3)
            {
                Cam.AngX = 5;
                PosInPlanet = 3;
            }
            if (e.KeyCode == Keys.NumPad4)
            {
                Cam.AngX = 5;
                PosInPlanet = 4;
            }
            if (e.KeyCode == Keys.NumPad5)
            {
                Cam.AngX = 5;
                PosInPlanet = 5;
            }
            if (e.KeyCode == Keys.NumPad6)
            {
                Cam.AngX = 5;
                PosInPlanet = 6;
            }
        }

        #region *** Передвижение камеры до позиции ******************************************

        static class Move
        {
            public static double Seconds;
            public static double ActSeconds;

            public static Vector3 StartPos;
            public static float StartAngX;
            public static float StartAngY;

            public static Vector3 ActPos;
            public static float ActAngX;
            public static float ActAngY;

            public static Vector3 DestPos;
            public static float DestAngX;
            public static float DestAngY;

            public static bool IsMove = false;
        }

        class CamMoveData
        {
            public double Seconds;

            public Vector3 DestPos;
            public float DestAngX;
            public float DestAngY;

            public CamMoveData(Vector3 DestPos, float DestAngX, float DestAngY, double Seconds)
            {
                this.DestPos = DestPos;
                this.DestAngX = DestAngX;
                this.DestAngY = DestAngY;
                this.Seconds = Seconds;
            }
        }

        void StartMove(Vector3 DestPos, float DestAngX, float DestAngY, double Seconds)
        {
            Move.Seconds = Seconds;
            Move.ActSeconds = 0;

            Move.StartPos = Cam.Pos;
            Move.StartAngX = Cam.AngX;
            Move.StartAngY = Cam.AngY;

            Move.ActPos = Cam.Pos;
            Move.ActAngX = Cam.AngX;
            Move.ActAngY = Cam.AngY;

            Move.DestPos = DestPos;
            Move.DestAngX = DestAngX;
            Move.DestAngY = DestAngY;

            Move.IsMove = true;
        }

        void CamMove(double ElapsedTime)
        {
            Move.ActSeconds += ElapsedTime;
            // коэффициент пройденного пути от 0 до 1
            if (Move.ActSeconds == 0) return;
            double koef = Move.ActSeconds / Move.Seconds;
            if (koef >= 1)
            {
                Cam.Pos = Move.DestPos;
                Cam.AngX = Move.DestAngX;
                Cam.AngY = Move.DestAngY;
                Move.IsMove = false;
                return;
            }

            float PosX = Move.StartPos.X + (float)((Move.DestPos.X - Move.StartPos.X) * koef);
            float PosY = Move.StartPos.Y + (float)((Move.DestPos.Y - Move.StartPos.Y) * koef);
            float PosZ = Move.StartPos.Z + (float)((Move.DestPos.Z - Move.StartPos.Z) * koef);

            Cam.Pos = new Vector3(PosX, PosY, PosZ);
            Cam.AngX = Move.StartAngX + (float)((Move.DestAngX - Move.StartAngX) * koef);
            Cam.AngY = Move.StartAngY + (float)((Move.DestAngY - Move.StartAngY) * koef);
        }

        #endregion *** Передвижение камеры до позиции ******************************************

    }
}
